﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//结构体
//struct S
//{
//	char c1;//1 8 ->1
//	int i;//4 8 ->4
//	char c2;//1 8 ->1
//};

//struct S2
//{
//	char c1;//1 8->1
//	char c2;//1 8 ->1
//	int i;//4 8 ->4
//};

//struct S3
//{
//	double d;//8 8 -> 8
//	char c;//1 8 ->1
//	int i;//4 8 ->4
//};

//struct S4
//{
//	char c1;
//	struct S3 s3;
//	double d;
//};

//结构体内存对齐
//int main()
//{
//	struct S s = {0};
//	struct S2 s = {0};
//	struct S3 s = {0};
//	struct S4 s = {0};
//	printf("%zd\n", sizeof(s));//12    size_t %zd   无符号整型
//	return 0;
//}

//结构体的对齐规则：
//1.结构体的第一个成员对齐和结构体变量起始变量位置偏移量为0的地址处
//2.其他成员变量要对对齐到某个数字（对齐数）的整数倍的地址处
//对齐数 = 编译器默认的一个对齐数 与 该成员变量大小的较小值
// - VS 中默认值为8
//-Linux中gcc没有默认对齐数，对齐数就是成员自身的大小
//3.结构体总大小为 最大对齐数 （结构体中每个成员变量都有一个对齐数，所有对齐数中最大的）的整数倍，
//4.如果嵌套了结构体的情况，嵌套的结构体成员对齐自己的成员中最大对齐数的整数倍处，结构体的整体大小就是所有最大对齐数（含嵌套结构体中成员的对齐数）的整数倍

//结构体内存对齐：总的来说：结构体内存对齐是拿空间换取时间的做法
//那在设计结构体的时候，我们既要满足对齐，又要节省空间，要如何做：让占⽤空间⼩的成员尽量集中在⼀起

//#pragma 这个预处理命令，可以改变对齐数
//#pragma pack(1) //设置默认对齐数为1

//#pragma pack()//取消设置的对齐数，还原为默认
//struct S
//{
//	char c1;//1 1 1
//	int i;//4 1 1
//	char c2;//1 1 1
//};
//
//int main()
//{
//	printf("%zd\n", sizeof(struct S));//输出结果为：6
//	return 0;
//}

//结构体传参
//struct S
//{
//	int arr[1000];
//	int n;
//	double d;
//};
//
//void print1(struct S tmp)
//{
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		printf("%d ", tmp.arr[i]);
//	}
//	printf("%d ", tmp.n);
//	printf("%lf\n", tmp.d);
//}
//
//void print2(const struct S* ps)
//{
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		printf("%d ", ps->arr[i]);
//	}
//	printf("%d ", ps->n);
//	printf("%lf\n", ps->d);
//}
//
//int main()
//{
//	struct S s = { {1,2,3,4,5},100,3.15 };
//	//print1(s);//传结构体
//	print2(&s);//传地址
//	return 0;
//}
//结构体传参首选传地址。原因如下：
// 函数传参的时候，参数是需要压栈，会有时间和空间傻瓜的系统开销。
// 如果传递一个结构体对象的时候，结构体过大，参数压栈的系统开销比较大， 所以会导致性能的下降。
//结论：结构体传参的时候，要传结构体的地址。

//结构体实现位段
//位段的声明和结构体是类似的，有两个不同：
//1. 位段的成员必须是 int、unsigned int 或signed int ，在C99中位段成员的类型也可以选择其他类型。
//2. 位段的成员名后边有⼀个冒号和⼀个数字。

//位段的内存分配
//1.位段的成员可以是int,unsigned int ,signed int或者char等类型
//2.位段的空间上是按照需要以4个字节（int）或者1个字节（char）的方式来开辟的。
//3.位段涉及很多不确定因素，位段是不跨平台的，注重可移植的程序应该避免使⽤位段。


//位段式的结构：位：二进制位
//位段式是专门用来节省内存的
//struct S
//{
//	int _a:2;//只占2个bit位
//	int _b:5;//只占5个bit位
//	int _c:10;//只占10个bit位
//	int _d:30;//只占30个bit位
//};
//int main()
//{
//	printf("%zd\n", sizeof(struct S));//8个字节
//	return 0;
//}

//1.申请到的一块内存中，从左向右使用，还是从右向左使用，是不确定的。
//2.剩余的空间，不足下一个成员使用的时候，是浪费掉，申请下一块空间。
//struct S
//{
//	char a : 3;
//	char b : 4;
//	char c : 5;
//	char d : 4;
//};
//int main()
//{
//	struct S s = { 0 };
//	s.a = 10;
//	s.b = 12;
//	s.c = 3;
//	s.d = 4;
//	printf("%zd\n", sizeof(struct S));//3
//	return 0;
//}

//32位和64位的机器上int的长度是4个字节
//16位机器int的长度是2个字节

//总结：跟结构相比，位段可以达到同样的效果，并且可以很好的节省空间，但是有跨平台的问题存在

//struct A
//{
//	int _a : 2;
//	int _b : 3;
//	int _c : 10;
//	int _d : 30;
//};

//不能对位段式成员取地址
//极致内存节省时，才会 用到位段
//int main()
//{
//	struct A sa = { 0 };
//	//scanf("%d",&(sa._b));//这是错误的
//
//	//正确示范
//	int b = 0;
//	scanf("%d", &b);
//	sa._b = b;
//	
//	return 0;
//}


//自定义类型：
//1.结构体  struct
//2.联合体  union
//3.枚举

//结构体								联合体
//struct								union
//多个成员								
//每个成员都有自己独立的空间			


//联合体（共用体）的特点：
//联合体也叫：共用体（它们共用一块空间），对于联合体来说，同一时间只能使用一个成员，否则会出现冲突。
//
//union Un
//{
//	char c;//1
//	int i;//4
//};
//
//int main()
//{
//	union Un u = { 0 };
//	printf("%zd\n", sizeof(u));//4
//
//	//同一块空间
//	printf("%p\n", &u);
//	printf("%p\n", &(u.i));
//	printf("%p\n", &(u.c));
//	return 0;
//}

//联合体的对齐数也得是最大数的整数倍
//union Un
//{
//	char c[5];//5，相当于放了5个char类型的变量 1 8 1
//	int i;//4 8 4
//};
//
////联合的大小至少是最大成员的大小
////当最大成员
//int main()
//{
//	union Un u = { 0 };
//	printf("%zd\n", sizeof(u));//8
//	return 0;
//}

//union Un
//{
//	short s[7];//14 8 2
//	int i;//4 8 4  -->16是4的倍数
//};
//int main()
//{
//	union Un u = { 0 };
//	printf("%zd\n", sizeof(u));//16
//	return 0;
//}


//使用联合体是可以节省空间的。举例：
//比如：我们要搞一个活动，要上线一个礼品兑换单，礼品兑换单中有三种商品：图书、杯子、衬衫
//每一种商品都有：库存量、价格、商品类型。（公共属性）
//特殊属性：
//图书：书名、作者、页数
//杯子：设计
//衬衫：设计、可选颜色、可选尺寸

//struct gift_list
//{
//	int stock_number;//库存量
//	double price;//定价
//	int item_type;//商品类型
//};
//
//union
//{
//	struct
//	{
//		char title[20];//书名
//		char author[20];//作者
//		int num_pages;//页数
//	}book;
//	struct Mug//类型名
//	{
//		char design[30];//设计
//	}mug;//变量名
//	struct//匿名
//	{
//		char design[30];//设计
//		int color;//颜色
//		int sizes;//尺寸
//	}shirt;
//}item;
//
//int main()
//{
//	struct gift_list g;
//	return 0;
//}


//写一个程序，判断当前机器是大端？还是小端？
//int Check()
//{
//	//方式一
//	int n = 1;//0x 00 00 00 01  //地址是从低到高放置的
//	return *(char*)&n;
//
//	//方式二
//	/*int n = 0x11223344;
//	char* p = (char*)&n;
//	if (*p == 0x44)
//		return 1;
//	else
//		return 0;*/
//}

//方式三：巧妙写法
int Check()
{
	union Un
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;//小端：返回1，大端：返回0
}
int main()
{
	int ret = Check();//小端返回1，大端返回0
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");

	return 0;
}